
#include "RtBase.h"
#include "RtHttpProxySetting.h"
#include "RtHttpURL.h"
#include "RtHttpMozillaProfile.h"
#include "RtByteStream.h"
#include "RtUtilClasses.h"
#include "RtChannelFile.h"

CRtHttpProxySettingWin32::CRtHttpProxySettingWin32()
{
	m_BrowserType = BROWSER_OTHER;
}

CRtHttpProxySettingWin32::~CRtHttpProxySettingWin32()
{
}

BOOL CRtHttpProxySettingWin32::Init()
{
	BOOL bRet = GetDefaultBrowser();
	switch (m_BrowserType)
	{
	case BROWSER_IE:
		bRet = GetIESetting();
		GetIEWpadSetting();
		break;
	case BROWSER_NETSCAPE1:
	case BROWSER_NETSCAPE2:
	case BROWSER_FIREFOX:
		bRet = GetNetscapeSetting(m_BrowserType);
		break;
	default:
		bRet = FALSE;
		break;
	}
	
	RT_INFO_TRACE("CRtHttpProxySettingWin32::Init, m_BrowserType=" << m_BrowserType 
		<< " m_strWpad=" << m_strWpad
		<< " m_strAutoConfigURL=" << m_strAutoConfigURL
		<< " m_strManual=" << m_strManual
		<< " m_strByPass=" << m_strByPass
		<< " bRet=" << bRet);

	if (!bRet) {
		m_strWpad.resize(0);
		m_strAutoConfigURL.resize(0);
		m_strManual.resize(0);
		m_strByPass.resize(0);
		m_BrowserType = BROWSER_OTHER;
	}
	return bRet;
}

BOOL CRtHttpProxySettingWin32::GetDefaultBrowser()
{
#ifdef RT_WIN32
	HKEY key;
	int ret = RegOpenKeyEx(HKEY_CLASSES_ROOT,
							"http\\shell\\open\\command",
							0,
							KEY_QUERY_VALUE,
							&key);
	if(ret != ERROR_SUCCESS) {
		RT_ERROR_TRACE("CRtHttpProxySettingWin32::GetDefaultBrowser, RegOpenKeyEx");
		return ret;
	}

	char str[MAX_PATH];
	DWORD len = sizeof(str);
	ret = RegQueryValueEx(key, "", 0, 0, (unsigned char*)str, &len);
	RegCloseKey(key);
	if (ret != ERROR_SUCCESS) {
		RT_ERROR_TRACE("CRtHttpProxySettingWin32::GetDefaultBrowser, RegQueryValueEx1");
		return ret;
	}
	
	if(rt_strcaserstr(str, "iexplore.exe"))
		m_BrowserType = BROWSER_IE;
	else if(rt_strcaserstr(str, "NETSCAPE.EXE"))
		m_BrowserType = BROWSER_NETSCAPE1;
	else if(rt_strcaserstr(str, "NETSCP.EXE") || rt_strcaserstr(str, "NETSCP6.EXE"))
		m_BrowserType = BROWSER_NETSCAPE2;
	else if (rt_strcaserstr(str, "FIREFOX.EXE"))
		m_BrowserType = BROWSER_FIREFOX;
	else {
		RT_INFO_TRACE("CRtHttpProxySettingWin32::GetDefaultBrowser, str=" << str);
		m_BrowserType = BROWSER_IE;//update at 2007.08.16, to support IE kernel browser--Jobs Chen
		ret = ERROR_SUCCESS;
	}
	return ret == ERROR_SUCCESS ? TRUE : FALSE;
#else
	m_BrowserType = BROWSER_NETSCAPE1;
	return TRUE;
#endif
}

BOOL CRtHttpProxySettingWin32::GetIESetting()
{
#ifdef RT_WIN32
	HKEY key;
	int ret = RegOpenKeyEx(HKEY_CURRENT_USER,
							"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings",
							0,
							KEY_QUERY_VALUE,
							&key);
	if (ret != ERROR_SUCCESS) {
		RT_ERROR_TRACE("CRtHttpProxySettingWin32::GetIESetting, RegOpenKeyEx");
		return FALSE;
	}

	char strTmpBuf[1024];
	unsigned long bEnable = 0;
	unsigned long len = sizeof(bEnable);
	ret = RegQueryValueEx(key, "ProxyEnable", 0, 0, (unsigned char*)&bEnable, &len);
	if (ret == ERROR_SUCCESS && bEnable) {
		len = sizeof(strTmpBuf);
		ret = RegQueryValueEx(key, "ProxyServer", 0, 0, (unsigned char*)strTmpBuf, &len);
		if (ret == ERROR_SUCCESS) {
			m_strManual.resize(0);
			if (!strstr(strTmpBuf, "ftp=") && !strstr(strTmpBuf, "gopher=") 
				&& !strstr(strTmpBuf, "http=") && !strstr(strTmpBuf, "https=")
				&& !strstr(strTmpBuf, "socks=") )
			{
				m_strManual = "http=";
			}
			m_strManual.append(strTmpBuf, len - 1);
			len = sizeof(strTmpBuf);
			ret = RegQueryValueEx(key, "ProxyOverride", 0, 0, (unsigned char*)strTmpBuf, &len);
			if (ret == ERROR_SUCCESS)
				m_strByPass.assign(strTmpBuf, len - 1);
		}
	}
	
	len = sizeof(strTmpBuf);
	ret = RegQueryValueEx(key, "AutoConfigURL", 0, 0, (unsigned char*)strTmpBuf, &len);
	if (ret == ERROR_SUCCESS) {
		m_strAutoConfigURL.assign(strTmpBuf, len - 1);
	} 

	RegCloseKey(key);
	return TRUE;
#else
	return FALSE;
#endif
}

BOOL CRtHttpProxySettingWin32::GetIEWpadSetting()
{
#ifdef RT_WIN32
	BOOL bRet = FALSE;
	HKEY key;
	LONG ret = RegOpenKeyEx(HKEY_CURRENT_USER,
							"Software\\Microsoft\\Windows\\CurrentVersion\\Internet Settings\\Connections",
							0,
							KEY_QUERY_VALUE,
							&key);
	if (ret != ERROR_SUCCESS) {
		RT_ERROR_TRACE("CRtHttpProxySettingWin32::GetIEWpadSetting, RegOpenKeyEx");
		return bRet;
	}

	char strTmpBuf[1024];
	DWORD len = sizeof(strTmpBuf);
	ret = RegQueryValueEx(key, "DefaultConnectionSettings", NULL, NULL, (LPBYTE)strTmpBuf, &len);
	if (ret == ERROR_SUCCESS) {
		bRet = TRUE;
	} else {
		RT_ERROR_TRACE("CRtHttpProxySettingWin32::GetIEWpadSetting, RegQueryValueEx1");
	}

	RegCloseKey(key);
	if (!bRet)
		return bRet;

	CRtDataBlockNoMalloc dbTmp(strTmpBuf, len);
	CRtByteStreamT<CRtDataBlockNoMalloc, CRtHostNetworkConvertorNull> byteStr(dbTmp);
	DWORD dwDummy, dwFlag;
	byteStr >> dwDummy;	// it should be "3C 00 00 00"
	byteStr >> dwDummy; // a counter
	byteStr >> dwFlag;  // flag
	
	CRtString strDummy, strWpad;
	byteStr.ReadStringWith4BytesLength(strDummy); // prxoy host and port
	byteStr.ReadStringWith4BytesLength(strDummy); // bypass
	byteStr.ReadStringWith4BytesLength(strDummy); // auto config pac file
	byteStr >> dwDummy;                           // don't know
	byteStr.ReadStringWith4BytesLength(strWpad);  // web proxy auto detect 
	
	if (!byteStr.IsGood()) {
		RT_ERROR_TRACE("CRtHttpProxySettingWin32::GetIEWpadSetting, wrong setting.");
		return FALSE;
	}

	if (dwFlag & 0x0008)
		m_strWpad = strWpad;
	return TRUE;
#else
	return FALSE;
#endif
}

BOOL CRtHttpProxySettingWin32::
GetNetscapeSetting(IRtHttpProxySetting::BrowseType aBrowserType)
{
	CRtString strRegistryPath;
	BOOL bRet = FALSE;

	CRtHttpMozillaProfile profileMozilla(aBrowserType);
	bRet = profileMozilla.GetCurrentProfileName(strRegistryPath);
	if (bRet) {
		bRet = FALSE;
		m_pNetscapePrefGet = new CNetscapePrefGet(this);
		if (m_pNetscapePrefGet && m_pNetscapePrefGet->Init(strRegistryPath)) 
			bRet = TRUE;
		else
			m_pNetscapePrefGet = NULL;
	}
	
	RT_INFO_TRACE("CRtHttpProxySettingWin32::GetNetscapeSetting,"
		" bRet=" << bRet << " strRegistryPath=" << strRegistryPath);
	return bRet;
}

BOOL CRtHttpProxySettingWin32::
GetProxySetting(CRtString &aWpad, CRtString &aAutoConfigURL, 
				CRtString &aManual, CRtString &aByPass, BrowseType &aType)
{
	BOOL bRet = Init();
	aWpad = m_strWpad;
	aAutoConfigURL = m_strAutoConfigURL;
	aManual = m_strManual;
	aByPass = m_strByPass;
	aType = m_BrowserType;
	return bRet;
}


/////////////////////////////////////////////////////////
// class CRtHttpProxySettingWin32::CNetscapePrefGet
/////////////////////////////////////////////////////////

CRtHttpProxySettingWin32::CNetscapePrefGet::
CNetscapePrefGet(CRtHttpProxySettingWin32* aSetting)
{
	m_pSetting = aSetting;
	RT_ASSERTE(m_pSetting);
}

CRtHttpProxySettingWin32::CNetscapePrefGet::~CNetscapePrefGet()
{
}

BOOL CRtHttpProxySettingWin32::CNetscapePrefGet::Init(const CRtString &aPathName)
{
	RtResult rv;
	BOOL bRet = FALSE;
	RT_ASSERTE_RETURN(!aPathName.empty(), bRet);

	CRtChannelFile *pFileChannel = new CRtChannelFile();
	if (!pFileChannel || !pFileChannel->Init(aPathName)) {
		delete pFileChannel;
		return bRet;
	}

	// callback will invoked in the AsyncOpen().
	BOOL bSync = TRUE;
	rv = pFileChannel->SetOption(RT_OPT_CHANNEL_FILE_SYNC, &bSync);
	RT_ASSERTE(RT_SUCCEEDED(rv));

	m_pFileChannel = pFileChannel;
	rv = m_pFileChannel->AsyncOpen(this);
	return RT_SUCCEEDED(rv) ? TRUE : FALSE;
}

static LPCSTR s_pMozillaPrefs[] = 
{
	"network.proxy.autoconfig_url",
	"network.proxy.http_port",
	"network.proxy.http",
	"network.proxy.no_proxies_on",
	"network.proxy.type",
	"network.proxy.ssl_port", // 5
	"network.proxy.ssl",
	"network.proxy.socks_version",
	"network.proxy.socks_port",
	"network.proxy.socks",
};

static int ParseMozillaPrefLine(LPCSTR aLine, CRtString &aResult)
{
	int nRet = -1;
	RT_ASSERTE_RETURN(aLine, nRet);
	
	if(strncmp(aLine,"user_pref", 9) != 0)
		return nRet;
	aLine += 9;
	if (*aLine++ != '(' || *aLine++ != '"')
		return nRet;
	
	LPCSTR p = aLine;
	for (nRet = 0; nRet < sizeof(s_pMozillaPrefs)/sizeof(s_pMozillaPrefs[0]); ++nRet) {
		size_t len = strlen(s_pMozillaPrefs[nRet]);
		if (!strncmp(aLine, s_pMozillaPrefs[nRet], len) && aLine[len] == '"') {
			p += len + 1;
			break;
		}
	}
	
	if (p == aLine || !*p)
		return -1;
	p++;	// skip ','
	while(*p == ' ' || *p == '\t')
		p++;

	LPCSTR pEnd = NULL;
	switch(nRet)
	{
	case 0:
	case 2:
	case 3:
	case 6:
	case 9:
		if (*p++ != '\"')
			break;
		pEnd = strchr(p,'\"');
		break;
	default:
		pEnd = strchr(p, ')');
		break;
	}

	if (pEnd)
		aResult.assign(p, pEnd - p);
	else
		nRet = -1;
	return nRet;
}

void CRtHttpProxySettingWin32::CNetscapePrefGet::
OnConnect(RtResult aReason, IRtChannel *aChannelId)
{
	RT_ASSERTE(m_pFileChannel.Get() == aChannelId);

	if (RT_FAILED(aReason))
		OnDisconnect(aReason, aChannelId);
}

void CRtHttpProxySettingWin32::CNetscapePrefGet::
OnReceive(CRtMessageBlock &aData, IRtTransport *aTrptId, 
		  CRtTransportParameter *aPara)
{
	RT_ASSERTE(m_pFileChannel.Get() == aTrptId);
	RT_ASSERTE(!aData.GetNext());
	RT_INFO_TRACE("CNetscapePrefGet::OnReceive, len=" << aData.GetChainedLength());

	if (aData.GetChainedLength() <= 0) {
		OnDisconnect(RT_ERROR_FAILURE, aTrptId);
		return;
	}

	int nProxytype = 0;
	CRtString strURL, strProxy, strProxyPort, strPass;
	CRtString strProxySSL, strPortSSL;
	CRtString strProxySocks, strPortSocks;
	int nSocksType = 5; //default is socks version5
	
	LPCSTR pLine = aData.GetTopLevelReadPtr(), pLineEnd;
	CRtString strLine, strContent;
	do {
		pLineEnd = strchr(pLine, '\n');
		if (pLineEnd)
			strLine.assign(pLine, pLineEnd - pLine);
		else
			strLine = pLine;

		int t = ParseMozillaPrefLine(strLine.c_str(), strContent);
		switch (t) {
			case 0:
				strURL = strContent;
				break;
			case 2:
				strProxy = strContent;
				break;
			case 1:
				strProxyPort = strContent;
				break;
			case 3:
				strPass = strContent;
				break;
			case 4:
				nProxytype = atoi(strContent.c_str());
				break;
			case 6:
				strProxySSL = strContent;
				break;
			case 5:
				strPortSSL = strContent;
				break;
			case 9:
				strProxySocks = strContent;
				break;
			case 8:
				strPortSocks = strContent;
				break;
			case 7:
				nSocksType = atoi(strContent.c_str());
				break;
		}

		if (pLineEnd)
			pLine = pLineEnd + 1;
	} while (pLineEnd);

	if (nProxytype == 1) {	// manual
		if (!strProxy.empty()) {
			m_pSetting->m_strManual += "http=";
			m_pSetting->m_strManual += strProxy;
			if (!strProxyPort.empty()) {
				m_pSetting->m_strManual += ':';
				m_pSetting->m_strManual += strProxyPort;
			}
			m_pSetting->m_strManual += ';';
		}
		if (!strProxySSL.empty()) {
			m_pSetting->m_strManual += "https=";
			m_pSetting->m_strManual += strProxySSL;
			if (!strPortSSL.empty()) {
				m_pSetting->m_strManual += ':';
				m_pSetting->m_strManual += strPortSSL;
			}
			m_pSetting->m_strManual += ';';
		}
		if (!strProxySocks.empty()) {
			if(5 ==nSocksType)
				m_pSetting->m_strManual += "socks5=";
			else
				m_pSetting->m_strManual += "socks=";
			m_pSetting->m_strManual += strProxySocks;
			if(!strPortSocks.empty()) {
				m_pSetting->m_strManual += ':';
				m_pSetting->m_strManual += strPortSocks;
			}
			m_pSetting->m_strManual += ';';
		}
		m_pSetting->m_strByPass = strPass;
	} 
	else if (nProxytype == 2) {	// auto config
		m_pSetting->m_strAutoConfigURL = strURL;
	}
	else if(nProxytype == 4)//wpad
	{
		m_pSetting->m_strWpad = "http://wpad/wpad.dat";
	}

	RT_INFO_TRACE("CNetscapePrefGet::OnReceive, nProxytype=" << nProxytype);
}

void CRtHttpProxySettingWin32::CNetscapePrefGet::
OnSend(IRtTransport *aTrptId, CRtTransportParameter *aPara)
{
	RT_ASSERTE(!"CNetscapePrefGet::OnSend");
}

void CRtHttpProxySettingWin32::CNetscapePrefGet::
OnDisconnect(RtResult aReason, IRtTransport *aTrptId)
{
	RT_ASSERTE(m_pFileChannel.Get() == aTrptId);
	if (m_pFileChannel) {
		m_pFileChannel->Disconnect(aReason);
		m_pFileChannel = NULL;
	}

	m_pSetting->m_pNetscapePrefGet = NULL;
}

